﻿#region Opis pliku
//-----------------------------------------------------------------------------
// EntityManager.cs
// Autor: Jan Fraś
// 30.10.2012
//-----------------------------------------------------------------------------
#endregion

#region using...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

#endregion

namespace pogra
{
    /// <summary>
    /// klasa przechowująca oraz zarządzająca jednostkami w grze.
    /// </summary>
    public class EntityManager
    {
        #region typy zagniezdzone

        /// <summary>
        /// struktra zwracająca ilość jednostek i budynków obu graczy - potrzebna do wyświwetlania statystyk na ekranie
        /// </summary>
        public struct Stats
        {
            public int UnitsACount;
            public int UnitsBCount;
            public int BuildingsACount;
            public int BuildingsBCount;
        }

        #endregion

        #region pola

        List<Unit> unitsA = new List<Unit>(), unitsB = new List<Unit>();
        List<Unit> chosenUnits = new List<Unit>();
        List<Buildings> chosenBuildings = new List<Buildings>();

        List<Buildings> buildingsA = new List<Buildings>(), buildingsB = new List<Buildings>();
        Random random = new Random();
        int formation = 0;
        private Texture2D tanktexA;
        private Texture2D factorytex;
        private Texture2D grunttex;
        private Texture2D barracktex;
        private Texture2D particletexture;
        public int kasagracza, kasakomputera;


        bool canSelect = true;

        #endregion

        #region wlasciwosci

        /// <summary>
        /// Wlasciwosc przechowujaca liczby jednostek kazdego gracza
        /// </summary>
        public Stats Stat
        {
            get
            {
                Stats tmp = new Stats();
                tmp.UnitsACount = unitsA.Count;
                tmp.UnitsBCount = unitsB.Count;
                tmp.BuildingsACount = buildingsA.Count;
                tmp.BuildingsBCount = buildingsB.Count;
                return tmp;
            }
        }

        /// <summary>
        /// Lista zaznaczonych jednostek
        /// </summary>
        public List<Unit> Selection
        {
            get { return new List<Unit>(chosenUnits); }
            set { chosenUnits = value; }
        }

        public int Kasa
        {
            get { return kasagracza; }
            set { kasagracza = value; }
        }

        public List<Unit> UnitAList
        {
            get { return unitsA; }
        }
        public List<Unit> UnitBList
        {
            get { return unitsB; }
        }

        public bool CanSelect
        {
            get { return canSelect; }
            set { canSelect = value; }
        }

        #endregion

        #region inicjalizacja

        /// <summary>
        /// na samym wstępie dodawane są budynki komputera oraz jednostki z którymi zaczyna potyczkę
        /// </summary>
        /// <param name="tanktexA"></param>
        /// <param name="helitex"></param>
        /// <param name="grunttex"></param>
        /// <param name="factorytex"></param>
        /// <param name="barracktex"></param>
        /// <param name="particletexture"></param>
        public EntityManager(Texture2D tanktexA, Texture2D helitex, Texture2D grunttex, Texture2D factorytex, Texture2D barracktex, Texture2D particletexture)
        {

            kasagracza = 30000;
            kasakomputera = 45000;
            this.tanktexA = tanktexA;
            this.factorytex = factorytex;
            this.grunttex = grunttex;
            this.barracktex = barracktex;
            this.particletexture = particletexture;

            for (int i = 0; i < 1; i++)
            {
                buildingsB.Add(new Factory(new Vector2(2100, 100), Rand.nextFloat(0, Math.PI), 2, unitsB, unitsA, tanktexA, factorytex, particletexture));
                buildingsB.Add(new Barracks(new Vector2(2120, 400), Rand.nextFloat(0, Math.PI), 2, unitsB, unitsA, grunttex, barracktex, particletexture));
                //unitsB.Add(new Helicopter(new Vector2(random.Next(80000, 80001), random.Next(200, 300)), (float)(random.NextDouble() * Math.PI * 2), 2, helitex, particletexture, unitsA));
                //unitsB.Add(new Helicopter(new Vector2(random.Next(80000, 80001), random.Next(200, 300)), (float)(random.NextDouble() * Math.PI * 2), 2, helitex, particletexture, unitsA));
                unitsB.Add(new Tank(new Vector2(random.Next(2100, 2200), random.Next(200, 300)), (float)(random.NextDouble() * Math.PI * 2), 2, tanktexA, particletexture, unitsA));
                unitsA.Add(new Helicopter(new Vector2(random.Next(0, 0), random.Next(200, 300)), (float)(random.NextDouble() * Math.PI * 2), 1, helitex, particletexture, unitsB));
          }
            buildingsB[0].MakeAnOrder(ref kasakomputera, 40);
            buildingsB[1].MakeAnOrder(ref kasakomputera, 80);
        }

        #endregion

        #region metody draw i update

        internal void Draw(ExtendedSpriteBatch  exSpriteBatch)
        {
            foreach (Unit asd in unitsA)
                asd.Draw(exSpriteBatch);

            foreach (Unit asd in unitsB)
            {
                asd.Draw(exSpriteBatch);
                asd.Mood = Unit.Moodes.Aggressive;
            }

            foreach (Buildings asd in buildingsA)
                asd.Draw(exSpriteBatch);

            foreach (Buildings asd in buildingsB)
                asd.Draw(exSpriteBatch);

        }

        internal void Update(GameTime gameTime)
        {
            //foreach (Entity asd in teamA)
            for (int i = 0; i < unitsA.Count; i++)
                unitsA[i].Update(gameTime.ElapsedGameTime);

            for (int i = 0; i < unitsB.Count; i++)
                unitsB[i].Update(gameTime.ElapsedGameTime);

            for (int i = 0; i < buildingsA.Count; i++)
                buildingsA[i].Update(gameTime.ElapsedGameTime);

            for (int i = 0; i < buildingsB.Count; i++)
                buildingsB[i].Update(gameTime.ElapsedGameTime);

            unitsA.RemoveAll(item => item.flag.RemoveMePlease == true);
            unitsB.RemoveAll(item => item.flag.RemoveMePlease == true);
        }

        #endregion

        #region metody publiczne

        public void SetFormation(int formation)
        {
            this.formation = formation;
        }

        public void SetSelectedUnitStance(Unit.Moodes mood)
        {
            foreach (Unit asd in chosenUnits)
            {
               asd.Mood = mood;
            }
        }

        internal void HandleMouse(Point mousePressedPos, Point mousePos, bool leftButton)
        {

            //decyzja czy kliknięcie czy zaznaczanie
            //zaznaczanie lewym
            if(leftButton == true/* && (Math.Pow(mousePressedPos.X - mousePos.X,2)+Math.Pow(mousePressedPos.Y - mousePos.Y,2)) > 100*/)
            {
                #region obsługa ujemych wymiarów
                if(mousePos.X<mousePressedPos.X)
                {
                    int tmp = mousePos.X;
                    mousePos.X = mousePressedPos.X;
                    mousePressedPos.X = tmp;
                }
             
                if(mousePos.Y<mousePressedPos.Y)
                {
                    int tmp = mousePos.Y;
                    mousePos.Y = mousePressedPos.Y;
                    mousePressedPos.Y = tmp;
                }
                #endregion

                foreach (Unit asd in chosenUnits)
                    {
                        asd.select(false);
                    }

                chosenUnits.Clear();
                foreach (Unit asd in unitsA)
                {
                    if (asd.Position.X > mousePressedPos.X && asd.Position.X < mousePos.X && asd.Position.Y > mousePressedPos.Y && asd.Position.Y < mousePos.Y && asd.Health>0)
                    {
                        asd.select(true);
                        chosenUnits.Add(asd);
                    }
                }
                //jeśli pusta, to może bydynek?
                if (chosenUnits.Count == 0)
                {
                    Vector2 tmpvec = new Vector2((float)mousePos.X, (float)mousePos.Y);

                    chosenBuildings.Clear();
                    foreach (Buildings asd in buildingsA)
                    {
                        if ((asd.Position - tmpvec).Length()< 30 && asd.Health > 0)
                        {
                            asd.MakeAnOrder(ref kasagracza);
                            break;
                        }
                    }
                }

            }

            if(leftButton == false)  //prawy
            {
                chosenUnits.RemoveAll(item => item.Health <= 0);

                if (chosenUnits.Count != 0)
                {
                    Unit tmp = findNearestUnit(unitsB, mousePos);
                    if(tmp != null)
                    {
                        setTarget(tmp);
                    }
                    else
                    {
                            chosenUnits[0].leaderSet(chosenUnits, mousePos, formation);
                            setGoalPosition(mousePos);
                    }
                }
            }
        }

        public int getUnitID(Unit unit)
        {
            Type type = unit.GetType();
            if(type == typeof(Grunt))
                return 0;
            else if(type == typeof(Tank))
                return 1;
            else if(type == typeof(Helicopter))
                return 2;

            return 0;
        }

        public int getBuildingID(Buildings building)
        {
            Type type = building.GetType();

            if (type == typeof(Barracks))
                return 0;
            if (type == typeof(Factory))
                return 1;

            return 0;
        }




        public Texture2D getBuildingTex(int ID)
        {
            if (ID == 0)
                return factorytex;
            if (ID == 1)
                return barracktex;
            else
                return null;
        }

        /// <summary>
        /// budowanie budynków przez gracza
        /// </summary>
        /// <param name="typ"></param>
        /// <param name="position"></param>
        /// <param name="rotation"></param>
        public void Build(int typ, Vector2 position, float rotation)
        {
            switch(typ)
            {
                case 0:
                    if (Factory.kosztBudowy <= kasagracza)
                    {
                        buildingsA.Add(new Factory(position, rotation, 1, unitsA, unitsB, tanktexA, factorytex, particletexture));
                        kasagracza -= Factory.kosztBudowy;
                    }
                    break;

                case 1:
                    if (Barracks.kosztBudowy <= kasagracza)
                    {
                        buildingsA.Add(new Barracks(position, rotation, 1, unitsA, unitsB, grunttex, barracktex, particletexture));
                        kasagracza -= Barracks.kosztBudowy;
                    }
                    break;
            }

        }

        #endregion

        #region metody prywatne

        private Unit findNearestUnit(List<Unit> a, Point mousePos)
        {
            Vector2 tmpvec = new Vector2((float)mousePos.X, (float)mousePos.Y);
            float distance = 20;
            Unit b = null;
            foreach (Unit asd in a)
            {
                if ((asd.Position - tmpvec).Length() < distance)
                {
                    distance = (asd.Position - tmpvec).Length();
                    b = asd;
                }
            }

            return b;
        }

        /// <summary>
        /// ustawianie celu do ataku
        /// </summary>
        /// <param name="a"></param>
        private void setTarget(Unit a)
        {
            foreach (Unit asd in chosenUnits)
            {
                asd.settarget(a);
            }
        }

        /// <summary>
        /// ustawianie miejsca docelowego
        /// </summary>
        /// <param name="mousePos"></param>
        private void setGoalPosition(Point mousePos)
        {

            if (formation != 0)
            {
                chosenUnits[0].setGoalPosition(mousePos);
                return;
            }

            Vector2 masscenter = new Vector2();
            foreach (Unit asd in chosenUnits)
            {
                masscenter += asd.Position;
            }

            masscenter /= chosenUnits.Count;

            float tmp = (float)(Math.Pow(chosenUnits.Count,0.5f)) * 20;
            
            foreach (Unit asd in chosenUnits)
            {
                //asd.setGoalPosition(new Vector2((float)mousePos.X, (float)mousePos.Y) + asd.Position - masscenter);
                asd.setGoalPosition(new Vector2((float)(mousePos.X + Rand.nextFloat(-tmp, tmp)), (float)(mousePos.Y + Rand.nextFloat(-tmp, tmp))));                
                //asd.Mood = Unit.Moodes.Defensive;
            }

            //SetSelectedUnitStance(Unit.Moodes.Aggressive);

        }
    #endregion

    }
}
